package com.iot.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.iot.commons.ResponseResult;
import com.iot.enums.ElectronicFenceContentEnum;
import com.iot.enums.LanguageEnum;
import com.iot.enums.StatusEnum;
import com.iot.mapper.DeviceDataLocationMapper;
import com.iot.mapper.FollowMapper;
import com.iot.mapper.MessageMapper;
import com.iot.model.dto.ElectronicFenceRecordDto;
import com.iot.model.entity.*;
import com.iot.model.query.DeviceDataLocationQuery;
import com.iot.model.dto.DeviceDataLocationDto;
import com.iot.model.query.FollowQuery;
import com.iot.model.vo.AuthInfoVo;
import com.iot.model.vo.DeviceDataLocationVo;
import com.iot.model.vo.UserInfoVo;
import com.iot.service.DeviceDataLocationService;
import com.iot.service.ElectronicFenceRecordService;
import com.iot.service.ElectronicFenceService;
import com.iot.service.UserInfoService;
import com.iot.utils.AppPushUtils;
import com.iot.utils.DateUtil;
import com.iot.utils.ElectronicFenceUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import com.iot.utils.ResponseResultUtil;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * DeviceDataLocationServiceImpl
 *
 * @author: dh
 * @date: 2024-04-21 11:49:52
 */
@Service
@RequiredArgsConstructor
public class DeviceDataLocationServiceImpl implements DeviceDataLocationService {

    private final FollowMapper followMapper;

    private final MessageMapper messageMapper;

    private final UserInfoService userInfoService;

    private final ElectronicFenceService electronicFenceService;

    private final DeviceDataLocationMapper deviceDataLocationMapper;

    private final ElectronicFenceRecordService electronicFenceRecordService;

    @Override
    public ResponseResult getList(DeviceDataLocationQuery deviceDataLocation) {
        AuthInfoVo authInfoEntity = (AuthInfoVo) StpUtil.getSession().get("loginInfo");
        if (null == authInfoEntity) {
            return ResponseResultUtil.authInfoFailure();
        }
        Page<DeviceDataLocationEntity> page = new Page<>(deviceDataLocation.getPageIndex(), deviceDataLocation.getPageSize());
        LambdaQueryWrapper<DeviceDataLocationEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(DeviceDataLocationEntity::getAuthId, authInfoEntity.getId());
        queryWrapper.eq(DeviceDataLocationEntity::getState, StatusEnum.VALID.getKey());
        if (null != deviceDataLocation.getStartTime()) {
            queryWrapper.ge(DeviceDataLocationEntity::getTime, deviceDataLocation.getStartTime());
        }
        if (null != deviceDataLocation.getEndTime()) {
            queryWrapper.le(DeviceDataLocationEntity::getTime, deviceDataLocation.getEndTime());
        }
        queryWrapper.orderByDesc(DeviceDataLocationEntity::getTime);
        IPage<DeviceDataLocationEntity> deviceDataLocationPage = deviceDataLocationMapper.selectPage(page, queryWrapper);
        List<DeviceDataLocationVo> deviceDataLocationVoList = Lists.newArrayList();
        for (DeviceDataLocationEntity deviceDataLocationEntity : deviceDataLocationPage.getRecords()) {
            DeviceDataLocationVo vo = new DeviceDataLocationVo();
            vo.setTime(deviceDataLocationEntity.getTime());
            vo.setLon(deviceDataLocationEntity.getLon());
            vo.setLat(deviceDataLocationEntity.getLat());
            deviceDataLocationVoList.add(vo);
        }

        return ResponseResultUtil.success(deviceDataLocationPage.getTotal(), deviceDataLocationVoList);
    }

    @Override
    public ResponseResult edit(DeviceDataLocationDto deviceDataLocation) {
        DeviceDataLocationEntity entity = new DeviceDataLocationEntity();
        entity.setTime(deviceDataLocation.getTime());
        entity.setLon(deviceDataLocation.getLon());
        entity.setLat(deviceDataLocation.getLat());
        deviceDataLocationMapper.updateById(entity);
        return ResponseResultUtil.success();
    }

    @Override
    public ResponseResult upload(List<DeviceDataLocationDto> deviceDataLocation, Long authId) {
        if (CollectionUtils.isEmpty(deviceDataLocation)) {
            return ResponseResultUtil.success();
        }
        if (null == authId) {
            return ResponseResultUtil.authInfoFailure();
        }
        UserInfoVo userInfoVo = userInfoService.getByAuthId(authId);
        if (null == userInfoVo) {
            return ResponseResultUtil.authInfoFailure();
        }

        List<DeviceDataLocationDto> newList = Lists.newArrayList();
        deviceDataLocation.stream().filter(distinctByKey(DeviceDataLocationDto::getTime))
                .forEach(newList::add);
        List<DeviceDataLocationEntity> entityList = Lists.newArrayList();
        for (DeviceDataLocationDto deviceDataLocationDto : newList) {
            if (null == deviceDataLocationDto.getTime() || StringUtils.isEmpty(deviceDataLocationDto.getLon())
                    || StringUtils.isEmpty(deviceDataLocationDto.getLat())) {
                return ResponseResultUtil.paramFailure();
            }

            LambdaQueryWrapper<DeviceDataLocationEntity> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(DeviceDataLocationEntity::getAuthId, authId);
            queryWrapper.eq(DeviceDataLocationEntity::getTime, deviceDataLocationDto.getTime());
            Long count = deviceDataLocationMapper.selectCount(queryWrapper);
            if (count > 0) {
                continue;
            }
            DeviceDataLocationEntity entity = new DeviceDataLocationEntity();
            entity.setAuthId(authId);
            entity.setTime(deviceDataLocationDto.getTime());

            entity.setLon(deviceDataLocationDto.getLon());
            entity.setLat(deviceDataLocationDto.getLat());
            entity.setState(StatusEnum.VALID.getKey());
            entity.setCreateTime(DateUtil.now());
            entityList.add(entity);
            check(deviceDataLocationDto, authId, userInfoVo);
        }
        if (CollectionUtils.isNotEmpty(entityList)) {
            deviceDataLocationMapper.insertList(entityList);
        }
        return ResponseResultUtil.success();
    }

    private void check(DeviceDataLocationDto dto, Long authId, UserInfoVo userInfoVo) {
        double lon = new BigDecimal(dto.getLon()).doubleValue();
        double lat = new BigDecimal(dto.getLat()).doubleValue();
        List<FollowEntity> followList = getFollowList(authId);
        if (CollectionUtils.isEmpty(followList)) {
            return;
        }
        followList = followList.stream().filter(x -> x.getElectronicFenceId() != null && x.getElectronicFenceType().equals(1)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(followList)) {
            return;
        }
        List<Long> electronicFenceIds = followList.stream().map(FollowEntity::getElectronicFenceId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(electronicFenceIds)) {
            return;
        }
        List<ElectronicFenceEntity> electronicFenceEntities = electronicFenceService.getByIds(electronicFenceIds);
        if (CollectionUtils.isEmpty(electronicFenceEntities)) {
            return;
        }
        Map<Long, ElectronicFenceEntity> groupById = electronicFenceEntities.stream().collect(Collectors.toMap(ElectronicFenceEntity::getId, x -> x, (obj1, obj2) -> obj2));
        for (FollowEntity followEntity : followList) {
            Long followAuthId = followEntity.getAuthId();
            ElectronicFenceEntity electronicFence = groupById.get(followEntity.getElectronicFenceId());
            if (null == electronicFence) {
                continue;
            }
            double electronicFenceLat = new BigDecimal(electronicFence.getLat()).doubleValue();
            double electronicFenceLon = new BigDecimal(electronicFence.getLon()).doubleValue();
            double radius = new BigDecimal(electronicFence.getRadius()).doubleValue();
            if (ElectronicFenceUtil.isInCircle(electronicFenceLat, electronicFenceLon, radius, lat, lon)) {
                UserInfoVo userInfo = userInfoService.getByAuthId(followAuthId);
                if (null == userInfo) {
                    continue;
                }

                String content = ElectronicFenceContentEnum.getByCode(dto.getLanguage())
                        .replace(LanguageEnum.NICKNAME.getValue(), StringUtils.isEmpty(userInfoVo.getNickName()) ? userInfoVo.getPhone() : userInfoVo.getNickName())
                        .replace(LanguageEnum.ELECTRONIC_FENCE.getValue(), electronicFence.getName());

                saveRecord(dto, electronicFence, StringUtils.isEmpty(userInfo.getNickName()) ? userInfo.getPhone() : userInfo.getNickName(),
                        followAuthId, followEntity.getId(),followEntity.getElectronicFenceId());
                sendMessage("围栏预警", content, null, 2, followAuthId, userInfoVo.getUid(), userInfo.getCid(), null);
            }
        }
    }

    private void saveRecord(DeviceDataLocationDto dto, ElectronicFenceEntity electronicFence, String nickName, Long authId, Long followId,Long fenceId) {
        ElectronicFenceRecordDto electronicFenceRecordDto = new ElectronicFenceRecordDto();
        electronicFenceRecordDto.setFollowId(followId);
        electronicFenceRecordDto.setNickName(nickName);
        electronicFenceRecordDto.setFenceId(fenceId);
        electronicFenceRecordDto.setAuthId(authId);
        electronicFenceRecordDto.setTime(dto.getTime());
        electronicFenceRecordDto.setFenceLon(electronicFence.getLon());
        electronicFenceRecordDto.setFenceLat(electronicFence.getLat());
        electronicFenceRecordDto.setFenceName(electronicFence.getName());
        electronicFenceRecordDto.setFenceRadius(electronicFence.getRadius());
        electronicFenceRecordDto.setLon(dto.getLon());
        electronicFenceRecordDto.setLat(dto.getLat());
        electronicFenceRecordService.add(electronicFenceRecordDto);
    }

    private void sendMessage(String title, String content, String param, Integer type, Long authId, Long uid, String cid, Long sid) {
        AppPushUtils.sendMessage(cid, "V Band", content);
        MessageEntity messageEntity = new MessageEntity();
        messageEntity.setTitle(title);
        messageEntity.setContent(content);
        messageEntity.setOtherParams(param);
        messageEntity.setType(type);
        messageEntity.setAuthId(authId);
        messageEntity.setUid(uid);
        messageEntity.setIsRead(1);
        messageEntity.setSid(sid);
        messageEntity.setState(StatusEnum.VALID.getKey());
        messageEntity.setCreateTime(DateUtil.nowSecond());
        messageMapper.insert(messageEntity);
    }

    private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object, Boolean> seen = Maps.newConcurrentMap();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    @Override
    public ResponseResult getById(DeviceDataLocationQuery deviceDataLocation) {
        DeviceDataLocationEntity deviceDataLocationEntity = deviceDataLocationMapper.selectById(deviceDataLocation.getId());
        DeviceDataLocationVo vo = new DeviceDataLocationVo();
        vo.setTime(deviceDataLocationEntity.getTime());
        vo.setLon(deviceDataLocationEntity.getLon());
        vo.setLat(deviceDataLocationEntity.getLat());
        return ResponseResultUtil.success(vo);
    }

    @Override
    public IPage<DeviceDataLocationEntity> getByDate(FollowQuery follow, Long followAuthId) {
        Page<DeviceDataLocationEntity> page = new Page<>(follow.getPageIndex(), follow.getPageSize());
        LambdaQueryWrapper<DeviceDataLocationEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(DeviceDataLocationEntity::getAuthId, followAuthId);
        queryWrapper.ge(DeviceDataLocationEntity::getTime, follow.getStartTime());
        if (null != follow.getEndTime()) {
            queryWrapper.le(DeviceDataLocationEntity::getTime, follow.getEndTime());
        }
        queryWrapper.orderByDesc(DeviceDataLocationEntity::getTime);
        return deviceDataLocationMapper.selectPage(page, queryWrapper);
    }

    private List<FollowEntity> getFollowList(Long authId) {
        LambdaQueryWrapper<FollowEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(FollowEntity::getFollowAuthId, authId);
        queryWrapper.eq(FollowEntity::getState, StatusEnum.VALID.getKey());
        return followMapper.selectList(queryWrapper);
    }

}
